// Copyright (c) 2008 screamingtarget@gmail.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.


#include "stdafx.h"
#include "resource.h"
#include "MainDlg.h"
#include <atltime.h>


LPCTSTR REG_ROOT = _T("Software\\WaveOutClient");
LPCTSTR REG_WINDOWPLACEMENT = _T("WindowPlacement");
LPCTSTR REG_IPADDRESS = _T("IpAddress");
LPCTSTR REG_PORT =_T("Port");


BOOL CMainDlg::PreTranslateMessage( MSG* pMsg )
{
	return CWindow::IsDialogMessage( pMsg );
}

BOOL CMainDlg::OnIdle()
{
	return FALSE;
}

LRESULT CMainDlg::OnInitDialog( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
	PlaceWindow();

	// set icons
	HICON hIcon = (HICON)::LoadImage( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDR_MAINFRAME ), 
		IMAGE_ICON, ::GetSystemMetrics( SM_CXICON ), ::GetSystemMetrics( SM_CYICON ), LR_DEFAULTCOLOR );
	SetIcon( hIcon, TRUE );
	HICON hIconSmall = (HICON)::LoadImage( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDR_MAINFRAME ), 
		IMAGE_ICON, ::GetSystemMetrics( SM_CXSMICON ), ::GetSystemMetrics( SM_CYSMICON ), LR_DEFAULTCOLOR );
	SetIcon( hIconSmall, FALSE );

	// register object for message filtering and idle updates
	CMessageLoop* pLoop = _Module.GetMessageLoop();
	ATLASSERT( pLoop != NULL );
	pLoop->AddMessageFilter( this );
	pLoop->AddIdleHandler( this );

	UIAddChildWindowContainer( m_hWnd );

   DoDataExchange();

   c_port.SetLimitText( 5 );

   RetrieveSettings();

   EnableStartButton();

   c_stop.EnableWindow( FALSE );

   SetConnectedText( false );

   dropouts_ = 0;
   
   SetDropoutText();

   if ( AllFieldsFilled() )
   {
      if ( PortFilled() )
      {
         c_start.SetFocus();
      }
      else
      {
         c_port.SetFocus();
      }

      return FALSE;
   }

	return TRUE;
}

LRESULT CMainDlg::OnDestroy( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   if ( StopClient_ )
   {
      StopClient_();
   }

   PersistWindowPlacement();
   PersistSettings();

	// unregister message filtering and idle updates
	CMessageLoop* pLoop = _Module.GetMessageLoop();
	ATLASSERT( pLoop != NULL );
	pLoop->RemoveMessageFilter( this );
	pLoop->RemoveIdleHandler( this );

	return 0;
}

LRESULT CMainDlg::OnCancel( WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
	CloseDialog( wID );
	return 0;
}

void CMainDlg::CloseDialog( int nVal )
{
	DestroyWindow();
	::PostQuitMessage( nVal );
}

LRESULT CMainDlg::OnSingleInstance( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   return RWM_SINGLEINSTANCE;
}


void CMainDlg::PersistWindowPlacement()
{
   CRegKey key;
   HRESULT hr = key.Create( HKEY_CURRENT_USER, REG_ROOT );

   if ( !SUCCEEDED( hr ) )
   {
      return;
   }

   WINDOWPLACEMENT wp;
   wp.length = sizeof( WINDOWPLACEMENT );

   GetWindowPlacement( &wp );

   key.SetBinaryValue( REG_WINDOWPLACEMENT, &wp, sizeof( WINDOWPLACEMENT ) );
}

void CMainDlg::PlaceWindow()
{
   CRegKey key;
   HRESULT hr = key.Create( HKEY_CURRENT_USER, REG_ROOT );

   if ( SUCCEEDED( hr ) )
   {
      WINDOWPLACEMENT wp;
      ULONG wpSize = sizeof( WINDOWPLACEMENT );

      if ( key.QueryBinaryValue( REG_WINDOWPLACEMENT, &wp, &wpSize ) == ERROR_SUCCESS )
      {
         wp.length = sizeof( WINDOWPLACEMENT );
         wp.flags = 0;
         wp.showCmd = ( wp.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : wp.showCmd );

         CRect rect;
         GetWindowRect( rect );

         int savedWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
         int savedHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;

         if ( rect.Width() != savedWidth )
         {
            wp.rcNormalPosition.right = wp.rcNormalPosition.left + rect.Width();
         }

         if ( rect.Height() != savedHeight )
         {
            wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + rect.Height();
         }

         SetWindowPlacement( &wp );

         return;
      }
   }

   CenterWindow();
}

void CMainDlg::PersistSettings()
{
   CRegKey key;
   HRESULT hr = key.Create( HKEY_CURRENT_USER, REG_ROOT );

   if ( !SUCCEEDED( hr ) )
   {
      return;
   }

   DWORD ipAddress = 0;
   int numFields = c_ipAddress.GetAddress( &ipAddress );

   if ( 4 == numFields )
   {
      key.SetDWORDValue( REG_IPADDRESS, ipAddress );
   }
   else
   {
      key.DeleteValue( REG_IPADDRESS );
   }
   
   ATL::CString port;
   c_port.GetWindowText( port );

   if ( !port.IsEmpty() )
   {
      int portVal = _ttoi( port );

      if ( portVal > 0 )
      {
         key.SetDWORDValue( REG_PORT, portVal );
      }
   }
   else
   {
      key.DeleteValue( REG_PORT );
   }
}

void CMainDlg::RetrieveSettings()
{
   CRegKey key;
   HRESULT hr = key.Create( HKEY_CURRENT_USER, REG_ROOT );

   if ( !SUCCEEDED( hr ) )
   {
      return;
   }

   DWORD ipAddress = 0;

   if ( key.QueryDWORDValue( REG_IPADDRESS, ipAddress ) == ERROR_SUCCESS )
   {
      c_ipAddress.SetAddress( ipAddress );
   }

   DWORD port = 0;

   if ( key.QueryDWORDValue( REG_PORT, port ) == ERROR_SUCCESS )
   {
      if ( port < MinPort )
      {
         port = MinPort ;
      }
      else if ( port > MaxPort )
      {
         port = MaxPort;
      }

      ATL::CString strPort;
      strPort.Format( _T("%d"), port );
      c_port.SetWindowText( strPort );
   }
}

void CMainDlg::EnableStartButton()
{
   BOOL enable = AllFieldsFilled() && PortFilled();
   c_start.EnableWindow( enable );
}

LRESULT CMainDlg::OnIpAddressChange( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
   EnableStartButton();
   return 0;
}

LRESULT CMainDlg::OnPortUpdate( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
   EnableStartButton();
   return 0;
}

LRESULT CMainDlg::OnPortKillFocus( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
   ATL::CString port;
   c_port.GetWindowText( port );

   if ( !port.IsEmpty() )
   {
      int portVal = _ttoi( port );

      if ( portVal < MinPort )
      {
         port.Format( _T("%d"), MinPort );
         c_port.SetWindowText( port );
      }
      else if ( portVal > MaxPort )
      {
         port.Format( _T("%d"), MaxPort );
         c_port.SetWindowText( port );
      }
   }

   return 0;
}


void CMainDlg::ClientProc( std::string ipAddress, std::string port )
{
   try
   {
      boost::asio::io_service io_service;

      tcp::resolver resolver( io_service );
      tcp::resolver::query query( ipAddress.c_str(), port.c_str() );
      tcp::resolver::iterator iterator = resolver.resolve( query );

      Client client( io_service, iterator, m_hWnd );

      StopClient_ = boost::bind( &Client::Stop, &client );

      io_service.run();    
   }
   catch ( std::exception& )
   {

   }

   StopClient_.clear();

   ::PostMessage( m_hWnd, RWM_CLIENTPROCEXIT, 0, 0 );
}

LRESULT CMainDlg::OnStartClicked( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
   stopPressed_ = false;
   EnableControls( FALSE );
   Connect();

   return 0;
}

LRESULT CMainDlg::OnStopClicked( WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/ )
{
   if ( StopClient_ )
   {
      stopPressed_ = true;
      StopClient_();
   }

   return 0;
}

void CMainDlg::EnableControls( BOOL enable /*= TRUE*/ )
{
   c_ipAddress.EnableWindow( enable );
   c_port.EnableWindow( enable );
   c_start.EnableWindow( enable );
}

LRESULT CMainDlg::OnClientProcExit( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   SetConnectedText( false );

   if ( !stopPressed_ )
   {
      Connect();
   }
   else
   {
      c_stop.EnableWindow( FALSE );
      EnableControls();
   }

   return 0;
}

void CMainDlg::Connect()
{
   ATL::CString ipAddress;
   c_ipAddress.GetWindowText( ipAddress );

   CT2CA pszIpAddress( ipAddress );

   ATL::CString port;
   c_port.GetWindowText( port );

   CT2CA pszPort( port );

   std::string strIpAddress( pszIpAddress );
   std::string strPort( pszPort );

   //thread_ = std::auto_ptr< boost::thread >( new boost::thread( boost::bind( &CMainDlg::ClientProc, this, strIpAddress, strPort ) ) );
   boost::thread thread( boost::bind( &CMainDlg::ClientProc, this, strIpAddress, strPort ) );
}

LRESULT CMainDlg::OnClientConnected( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   SetConnectedText();
   return 0;
}

void CMainDlg::SetConnectedText( bool connected /*= true*/ )
{
   if ( connected )
   {
      c_connected.SetWindowText( _T("Connected") );
   }
   else
   {
      c_connected.SetWindowText( _T("") );
   }
}

bool CMainDlg::AllFieldsFilled()
{
   DWORD ipAddress = 0;
   return ( c_ipAddress.GetAddress( &ipAddress ) == 4 );
}

bool CMainDlg::PortFilled()
{
   ATL::CString port;
   c_port.GetWindowText( port );
   return ( !port.IsEmpty() );
}

LRESULT CMainDlg::OnClientStarted( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   c_stop.EnableWindow( TRUE );
   return 0;
}

LRESULT CMainDlg::OnAudioDropout( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   ++dropouts_;
   SetDropoutText();
   return 0;
}

void CMainDlg::SetDropoutText()
{
   ATL::CString text;
   text.Format( _T("Dropouts: %d"), dropouts_ );
   c_dropouts.SetWindowText( text );
}

LRESULT CMainDlg::OnPortAudioStatus( UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
   ATL::CTime time = ATL::CTime::GetCurrentTime();
   ATL::CString timeString = time.Format( _T("%Y-%m-%d %H:%M:%S :  ") );

   ATL::CString status;

   if ( wParam & paInputUnderflow )
   {
      status += _T("Input Underflow");
   }

   if ( wParam & paInputOverflow )
   {
      if ( !status.IsEmpty() )
      {
         status += _T(", ");
      }

      status += _T("Input Overflow");
   }

   if ( wParam & paOutputUnderflow )
   {
      if ( !status.IsEmpty() )
      {
         status += _T(", ");
      }

      status += _T("Output Underflow");
   }

   if ( wParam & paOutputOverflow )
   {
      if ( !status.IsEmpty() )
      {
         status += _T(", ");
      }

      status += _T("Output Overflow");
   }

   if ( wParam & paPrimingOutput )
   {
      if ( !status.IsEmpty() )
      {
         status += _T(", ");
      }

      status += _T("Priming Output");
   }

   c_status.AddString( timeString + status );

   return 0;
}
